<template>
    <el-row :gutter="10" class="main">
        <el-col :xs="24" :sm="24" class="result hidden-md-and-up" v-show="totaldomains > 0">
            <el-card class="box-card" shadow="hover">
                <template #header>
                    <div class="card-header">
                        <span>Unregiested ({{okdomains.length}})</span>
                        <el-button size="mini" @click="Copy" :disabled="okdomains.length == 0" 
                            type="text" icon="el-icon-document-copy">{{copyText}}</el-button>
                    </div>
                </template>
                <textarea ref="hiddenipt" style="position: fixed;top: 0;left:0;width:1px;height:1px;padding:0;border:none;background:transparent"></textarea>
                <ul>
                    <li v-for="item in okdomains" :key="item">
                        <el-button size="mini" type="text">{{item}}</el-button>
                    </li>
                    <li v-show="okdomains.length == 0">empty list</li>
                </ul>
            </el-card>
        </el-col>
        <el-col :xs="24" :sm="24" :md="10" :span="10" class="tlds">
            <el-card class="box-card" shadow="hover">
                <template #header>
                    <div class="card-header">
                        <h1>Domain Checker</h1>
                        <el-button class="headerbtn hidden-sm-and-down" type="success" size="mini"
                         @click="Start" :disabled="chkdisabled">
                            Start <span align="right" v-show="totaldomains > 0">({{showsize(domains.length)}} / {{showsize(totaldomains)}})</span>
                        </el-button>
                    </div>
                </template>
                <el-form label-position="top">
                    <el-form-item label="Rules (*) a-z, 0-9; (?) a-z; ($) 0-9">
                        <el-input type="textarea" name="rule" :rows="3" v-model="rule" :placeholder="rulePlaceholder"></el-input>
                    </el-form-item>
                    <el-form-item>
                        <el-button class="hidden-md-and-up" style="width: 100%" type="success"
                         @click="Start" :disabled="chkdisabled">
                            Start <span align="right" v-show="totaldomains > 0">({{showsize(domains.length)}} / {{showsize(totaldomains)}})</span>
                        </el-button>
                    </el-form-item>
                    <textarea ref="hiddenipt" style="position: fixed;top: 0;left:0;width:1px;height:1px;padding:0;border:none;background:transparent"></textarea>
                </el-form>
            </el-card>
            <el-card class="box-card alltlds" :body-style="{padding:0}" shadow="hover">
                <template #header>
                    <div class="card-header">
                        <strong class="hidden-sm-and-down">Domain TLDs</strong>
                        <div>
                            <el-input type="text" placeholder="search tlds" v-model="filter" @input="doFilter" size="mini">
                            </el-input>
                        </div>
                        <div>
                            <el-button type="primary" class="hidden-sm-and-down" size="mini" :disabled="chkdisabled" @click="chooseAll()">Choose all</el-button>
                            <el-button type="info" size="mini" :disabled="tlds.length == 0" @click="clearAll()">Clear</el-button>
                        </div>
                    </div>
                </template>
                <el-checkbox-group v-model="tlds">
                    <el-tabs v-model="tldmodel" type="border-card">
                        <el-tab-pane :name="item.name" :label="item.title ? item.title : item.name" v-for="item in filterdTlds" :key="item">
                            <div>
                                <el-button type="primary" size="mini" :disabled="chkdisabled" @click="chooseSubAll(item.name)">choose all</el-button>
                                <ul>
                                    <li v-for="tld in item.list" :key="tld">
                                        <el-checkbox type="checkbox" :label="tld">{{tld}}</el-checkbox>
                                    </li>
                                </ul>
                            </div>
                        </el-tab-pane>
                    </el-tabs>
                </el-checkbox-group>
            </el-card>
        </el-col>
        <el-col :xs="24" :sm="24" :md="4" :span="4" class="result hidden-sm-and-down">
            <el-card class="box-card" shadow="hover">
                <template #header>
                    <div class="card-header">
                        <span>Unregiested ({{okdomains.length}})</span>
                        <el-button size="mini" @click="Copy" :disabled="okdomains.length == 0" 
                            type="text" icon="el-icon-document-copy">{{copyText}}</el-button>
                    </div>
                </template>
                <ul>
                    <li v-for="item in okdomains" :key="item">
                        <el-button size="mini" type="text">{{item}}</el-button>
                    </li>
                    <li v-show="okdomains.length == 0">empty list</li>
                </ul>
            </el-card>
        </el-col>
        <el-col :xs="24" :sm="24" :md="10" :span="10" class="rules">
            <el-card class="box-card logs" :body-style="{padding:0}" shadow="hover">
                <template #header>
                    <div class="card-header">
                        Logs
                        <el-button size="mini" @click="logs.length = 0" :disabled="logs.length == 0">Clear</el-button>
                    </div>
                </template>
                <ul>
                    <li v-for="item in logs" :key="item">{{item}}</li>
                </ul>
            </el-card>
        </el-col>
    </el-row>
</template>

<script>
export default {
    data() {
        return {
            totaldomains: 0,
            tldmodel: 'Popular',
            chkdisabled: false,
            maxWS: 1,
            maxLogs: 50,
            maxsize: 20,
            rulePlaceholder: '??ok?\n**ok\n$$11',
            alltlds: [{name:"Popular",list:["agency","ai","best","biz","chat","club","co","com","dev","digital","email","global","group","inc","io","life","live","media","net","network","news","org","sale","solutions","studio","today","vip","world","xyz","zone"]},{name:"$2 or less",list:["agency","club","fun","host","icu","info","life","live","online","press","pro","pw","rocks","services","site","space","today","us","website","world","xyz"]},{name:"Academic",list:["academy","college","courses","degree","education","guide","help","how","institute","mba","school","science","study","support","tips","training","university","wiki"]},{name:"Adult ",list:["adult","cam","porn","sex","sexy","webcam","xxx"]},{name:"Arts &amp; Culture",list:["actor","art","dance","design","gallery","style","theater"]},{name:"Audio &amp; Video",list:["camera","chat","digital","fm","live","stream","tube","tv","video","webcam"]},{name:"Businesses",list:["associates","biz","boutique","business","company","enterprises","holdings","inc","industries","limited","llc","ltd","management","partners","ventures","work","works"]},{name:"Colors",list:["black","blue","green","pink","red"]},{name:"Finance",list:["accountant","accountants","capital","cash","claims","credit","creditcard","finance","financial","fund","inc","insure","investments","loan","loans","money","mortgage","tax"]},{name:"Food &amp; Drink",list:["bar","beer","cafe","catering","coffee","cooking","fish","kitchen","menu","organic","pizza","pub","recipes","rest","restaurant","vin","vodka","wine"]},{name:"Fun",list:["buzz","cool","fail","fun","lol","ninja","plus","rocks","vip","wtf"]},{name:"Health &amp; Fitness",list:["care","clinic","coach","dental","fit","fitness","health","healthcare","physio","surgery","vet","vision"]},{name:"International ",list:["ac","ae.org","africa","asia","berlin","br.com","bz","ca","cc","ch","cm","co.bz","co.uk","com.au","com.de","com.es","com.mx","com.pe","com.se","com.sg","com.vc","cx","cymru","de","de.com","es","eu","eu.com","fr","gb.net","gr.com","hamburg","hu.net","id","in","in.net","jp.net","jpn.com","kiwi","krd","kyoto","la","lat","li","london","me.uk","melbourne","mex.com","mx","nagoya","net.au","net.pe","nu","nyc","okinawa","org.au","org.es","org.mx","org.pe","org.uk","org.vc","osaka","paris","pe","pw","ru.com","ryukyu","sa.com","se.net","sg","sh","sydney","tokyo","uk","uk.com","uk.net","us","us.com","us.org","vc","vegas","wales","ws","za.com"]},{name:"Lifestyle",list:["careers","date","dating","eco","faith","family","lgbt","life","love","party","singles","travel","wedding"]},{name:"Marketing &amp; Sales",list:["agency","direct","market","marketing","promo","social","sucks"]},{name:"Media &amp; Music",list:["band","graphics","media","movie","observer","photography","photos","pics","pictures","press","productions","studio"]},{name:"Organizations",list:["camp","church","community","foundation","group","inc","org"]},{name:"Personal",list:["blog","expert","guru","me","mom","soy","space"]},{name:"Products",list:["auto","clothing","diamonds","energy","furniture","glass","gold","horse","jewelry","lighting","parts","shoes","solar","tires","toys","watch"]},{name:"Professional",list:["attorney","ceo","consulting","dentist","engineer","engineering","inc","lawyer","pro"]},{name:"Real Estate",list:["apartments","condos","country","estate","farm","forsale","house","land","lease","properties","realty","rent","rentals","villas"]},{name:"Services",list:["cab","cleaning","delivery","equipment","events","florist","garden","ink","legal","limo","protection","repair","salon","security","services","supplies","taxi"]},{name:"Shopping",list:["auction","bargains","bid","cheap","codes","coupons","deals","discount","exchange","fashion","gift","gifts","luxury","review","reviews","sale","shop","shopping","store"]},{name:"Sports &amp; Hobbies",list:["bet","bike","bingo","cards","club","cricket","fans","fishing","football","game","games","gg","golf","hockey","poker","racing","rodeo","run","ski","soccer","surf","team","tennis","yoga"]},{name:"Technology",list:["app","cloud","computer","dev","domains","download","email","gg","host","io","link","net","network","software","solutions","systems","tech","technology"]},{name:"Trades &amp; Construction",list:["build","builders","construction","contractors","plumbing","supply","tools","trade"]},{name:"Travel",list:["casino","cruises","flights","holiday","tours","travel","vacations","voyage"]},{name:"Miscellaneous",list:["ai","airforce","army","baby","bio","casa","center","city","click","cn.com","co.com","com","democrat","desi","directory","doctor","dog","earth","exposed","express","film","futbol","fyi","gdn","gives","gmbh","gratis","gripe","haus","immo","immobilien","international","irish","is","jetzt","kaufen","kim","ltda","maison","memorial","men","mobi","moda","moe","name","navy","net.vc","nom.es","one","onl","page","photo","rehab","reise","reisen","report","republican","rip","sarl","schule","shiksha","show","tel","tienda","to","top","town","uno","viajes","vote","voting","voto","win","xn","yokohama"]}],
            filterdTlds: [],
            tlds: ['com'],
            rule: '',
            filter: '',
            filter_timer: null,
            okdomains: [],
            copyText: 'Copy',
            message: 'text',
            letters: ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"],
            letternums: ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","0","1","2","3","4","5","6","7","8","9"],
            nums: ["0","1","2","3","4","5","6","7","8","9"],
            logs: [],
            wsu: ["w","s","s",":","/","/","d","o","m","a","i","n","s","-","w","s",".","r","e","v","v","e","d",".","c","o","m","/","v","1","/","w","s","?","b","a","t","c","h","=","t","r","u","e"],
            clients: {},
            domains: [],
            exited: false
        }
    },
    methods: {
        showsize(s) {
            if(s > 1000000) {
                return `${Math.floor( s / 100000) / 10}m`;
            }
            if(s > 1000) {
                return `${Math.floor( s / 100) / 10}k`;
            }
            return s;
        },
        Start() {
            if(this.rule.length == 0) {
                alert('rule required');
                return;
            }
            if(this.tlds.length == 0) {
                alert('TLD required');
                return;
            }
            this.chkdisabled = true;
            this.domains = this.buildDomains(this.rule.split("\n"), this.tlds);
            this.totaldomains += this.domains.length;
            this.chkdisabled = false;
        },
        Copy(){
            try {
                this.copy2Clipboard(this.okdomains.join('\n'));
                this.copyText = 'Copied';
                setTimeout(() => {
                    this.copyText = 'Copy'
                }, 3000);
                console.log('Copied to clipboard')
            } catch (e) {
                console.error(e)
            }
        },
        copy2Clipboard(e) {
            window.clipboardData ? (window.clipboardData.clearData(), window.clipboardData.setData("Text", e)) :
                this.setClipboardText(e)
        },
        setClipboardText(e) {
            this.$refs.hiddenipt.value = e;
            this.$refs.hiddenipt.select();
            try {
                document.execCommand("copy")
            } catch (e) {}
        },
        doFilter() {
            if(this.filter_timer != null) {
                clearTimeout(this.filter_timer);
            }
            if(this.filter.length == 0) {
                this.tlds = []
                this.filterdTlds = this.alltlds;
                return;
            }
            this.filter_timer = setTimeout(() => {
                this.filterdTlds = [];
                this.alltlds.forEach(element => {
                    let tlds = []
                    element.list.forEach(x => {
                        if(x.indexOf(this.filter) >= 0) {
                            tlds.push(x);
                        }
                    })
                    if(tlds.length > 0) {
                        this.filterdTlds.push({name: element.name, title: `${element.name} (${tlds.length})`, list: tlds});
                    }
                });
            }, 200)
        },
        chooseSubAll(name) {
            this.chkdisabled = true;
            let item = this.filterdTlds.find(x => x.name == name);
            if(!item) {
                return;
            }
            item.list.forEach(x => {
                if(this.tlds.indexOf(x) >= 0) {
                    this.tlds.splice(this.tlds.indexOf(x), 1);
                } else {
                    this.tlds.push(x)
                }
            })
            this.chkdisabled = false;
        },
        clearAll() {
            this.tlds = [];
        },
        chooseAll() {
            this.chkdisabled = true;
            if(this.tlds.length > 0) {
                this.tlds = []
            } else {
                this.tlds = []
                this.filterdTlds.forEach(element => {
                    element.list.forEach(x => {
                        this.tlds.push(x)
                    })
                });
            }
            this.chkdisabled = false;
        },
        log(txt) {
            this.logs.unshift(txt);
            if(this.logs.length > this.maxLogs) this.logs.pop();
            //console.log(txt)
        },
        addokdomains(name) {
            this.okdomains.unshift(name);
        },
        connect(i) {
            let ws = new WebSocket(this.wsu.join(""));
            ws.onopen = (() => {
                this.log(`worker ${i} ready`);
            });
            ws.onmessage = (e => {
                let t = JSON.parse(e.data);
                if(t.data.name && this.clients[`worker${i}`]) {
                    this.clients[`worker${i}`].domains.splice(this.clients[`worker${i}`].domains.indexOf(t.data.name), 1);
                    this.log(`received: ${e.data}`);
                    if(t.data.available && !t.data.premium) {
                        this.addokdomains(t.data.name);
                    }
                }
            });
            ws.onclose = (e => {
                ws = null
                if(this.clients[`worker${i}`].domains.length > 0) {
                    this.domains.push(...this.clients[`worker${i}`].domains);
                    this.clients[`worker${i}`].domains = [];
                }
                clearInterval(this.clients[`worker${i}`].timer);
                if(this.exited) return;
                setTimeout(() => {
                    this.clients[`worker${i}`].ws = this.connect(i);
                }, 3000)
                this.log("exit!")
            })
            ws.onerror = (e => {
                this.log("error: " + e);
            });
            return ws;
        },
        buildDomains(e, t) {
            let o, n;
            for (o = [], n = 0; n < e.length; n++) {
                let a = e[n];
                if (a.length > 0) {
                    let s, i;
                    for (s = [], i = 0; i < a.length; i++) switch (a.substr(i, 1)) {
                        case "*":
                            s.push(this.letternums);
                            break;
                        case "?":
                            s.push(this.letters);
                            break;
                        case "$":
                            s.push(this.nums);
                            break;
                        default:
                            s.push([a.substr(i, 1)])
                    }
                    let l = 1;
                    for (i = 0; i < s.length; i++) l *= s[i].length;
                    if (l * t.length > 5e7) return alert("unchecked domains more than 10000/" + l * t.length), o;
                    let r = 0;
                    for (i = 0; i < l; i++) {
                        let c = 1, u, d;
                        r = l;
                        for (u = [], d = 0; d < s.length; d++) {
                            let m = 0;
                            s[d].length > 1 && (c = i % r, r /= s[d].length, m = parseInt(c / r)), u.push(s[d][m])
                        }
                        u.push(".");
                        let h = u.join("");
                        if (u = [], h.length > 0)
                            for (let p = 0; p < t.length; p++) {
                                let g = h + t[p];
                                o.push(g)
                            }
                    }
                }
            }
            return o
        }
    },
    created() {
        this.filterdTlds = this.alltlds;
        for(let i = 0; i < this.maxWS; i++) {
            this.clients[`worker${i}`] = {
                ws: this.connect(i),
                domains: []
            }
            this.clients[`worker${i}`].timer = setInterval(() => {
                //console.log('xxx', this.domains.length, this.clients[`worker${i}`].ws);
                if(this.domains.length > 0 && this.clients[`worker${i}`].ws && this.clients[`worker${i}`].ws.readyState == 1) {
                    let e = this.domains.splice(0, this.maxsize);
                    this.clients[`worker${i}`].domains.push(...e);
                    let t = {
                        type: "domainStatus",
                        reqID: Date.parse(new Date).toString(),
                        data: {
                            domains: e
                        }
                    };
                    //this.log(`send: ${JSON.stringify(t)}`);
                    this.clients[`worker${i}`].ws.send(JSON.stringify(t))
                    t = {}
                }
            }, 100)
        }
    },
    unmounted() {
        this.exited = true;
        this.domains = [];
        this.okdomains = [];
        for(let i in this.clients) {
            clearInterval(this.clients[i].timer);
            this.clients[i].ws.close();
            this.clients[i].ws = null;
            this.clients[i].domains = [];
        }
    }
}
</script>

<style>
.tlds{
    text-align: left;
}
.rules{
    text-align: left;
}
.result{
    text-align:left;
    margin-bottom:20px;
}
.result ul{
    margin:0;
    padding:0;
    list-style: none;
}
.tlds ul{
    margin:0;padding:5px;
    list-style: none;
}
.tlds ul li{
    margin:5px;width:85px;text-align:left;background-color:#FAFAFA;
    display:inline-block
}
.tlds ul li label{
    padding:5px;
    display: block;
}
.alltlds{
    margin-top:20px;
    margin-bottom:20px;
}
.logs ul{
    list-style:none;
    margin:0;padding:0;
}
.logs ul li{
    text-align: left;
    padding:3px;
}
.card-header {
      display: flex;
      justify-content: space-between;
      align-items: center;
}
.card-header h1{margin:0;padding:0}
.card-header .headerbtn{margin:0}
</style>